package kcptest

import (
	"github.com/wonderivan/logger"
	"github.com/xtaci/kcp-go"
	"io"
	"strconv"
	"sync"
	"sync/atomic"
	"time"
)

type KcpServer struct {
	counter        uint32
	handler        KcpEventHandler
	lockOfConnInfo sync.Mutex
	mapOfConnInfo  map[*kcp.UDPSession]chan []byte
}

func NewKcpServer() *KcpServer {
	return &KcpServer{
		counter:        0,
		lockOfConnInfo: sync.Mutex{},
		mapOfConnInfo:  make(map[*kcp.UDPSession]chan []byte, 100),
	}
}

func (ks *KcpServer) Start(port int, handler KcpEventHandler) {
	logger.Debug("KcpServer Start Port:%d", port)
	lAddr := "0.0.0.0:" + strconv.Itoa(port)
	ks.handler = handler
	listener, err := kcp.ListenWithOptions(lAddr, nil, 10, 3)
	if err != nil {
		panic(err)
	}
	listener.SetDSCP(46)
	err = listener.SetReadBuffer(16 * 1024 * 1024)
	if err != nil {
		panic(err)
	}
	err = listener.SetWriteBuffer(16 * 1024 * 1024)
	if err != nil {
		panic(err)
	}
	go ks.runListener(listener)
}

func (ks *KcpServer) runListener(listener *kcp.Listener) {
	logger.Debug("runListener")
	defer listener.Close()
	for {
		conn, err := listener.AcceptKCP()
		if err != nil {
			panic(err.Error())
		}
		//logger.Debug("conn:%s", conn.RemoteAddr())
		go ks.runConnRead(conn)
	}
}

func (ks *KcpServer) runConnRead(conn *kcp.UDPSession) {
	readBuf := make([]byte, 65535)
	conn.SetNoDelay(1, 10, 2, 1)
	conn.SetStreamMode(true)
	conn.SetWindowSize(1024, 1024)
	conn.SetMtu(1400)
	conn.SetACKNoDelay(false)
	for {
		readLen, err := conn.Read(readBuf)
		//logger.Debug("runConnRead len=%d msg=%s", readLen, readBuf[:readLen])
		if err == nil {
			go func(con *kcp.UDPSession) {
				_, err = con.Write(readBuf[:readLen])
				newCount := atomic.AddUint32(&ks.counter, 1)
				//logger.Debug("newCount=%d", newCount)
				if newCount == 1 {
					logger.Debug("startTime=%v", time.Now().UnixNano()/1e6)
				}
				if newCount == 100*10000 {
					logger.Debug("endTime=%v", time.Now().UnixNano()/1e6)
				}
			}(conn)
		} else if err == io.EOF {
			logger.Debug("runConnRead EOF:%s", conn.RemoteAddr())
			break
		} else {
			logger.Debug("runConnRead ERROR:%s", err.Error())
			break
		}
	}
}
